package com.atwy.java8;

import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @Author: 小王子火
 * @Date: 2022/4/2
 * <p>
 * Stream流的练习
 */
public class StreamPractice {
    public static void main(String[] args) {
        Trader raoul = new Trader("Raoul", "Cambridge");
        Trader mario = new Trader("Mario", "Milan");
        Trader alan = new Trader("Alan", "Cambridge");
        Trader brian = new Trader("Brian", "Cambridge");
        List<Transaction> transactions = Arrays.asList(
                new Transaction(brian, 2011, 300),
                new Transaction(raoul, 2012, 1000),
                new Transaction(raoul, 2011, 400),
                new Transaction(mario, 2012, 710),
                new Transaction(mario, 2011, 700),
                new Transaction(alan, 2012, 950)
        );
        //BigDecimal total = BigDecimal.ZERO;
        final Map<String,Boolean> flag = new HashMap<>(1);
        final boolean[] arrayFlag = new boolean[1];
        flag.put("flag",false);
        System.out.println("flag: --->"+flag.get("flag"));
        System.out.println("arrayFlag: --->"+arrayFlag[0]);
        transactions.stream().forEach(transaction -> {
            if (transaction.getValue() == 300) {
                flag.put("flag",true);
                arrayFlag[0] = true;
            }
        });
        System.out.println("flag过滤之后="+flag.get("flag"));
        System.out.println("arrayFlag: --->"+arrayFlag[0]);


        //(1) 找出2011年发生的所有交易，并按交易额排序（从低到高）。
        System.out.println("1、找出2011年发生的所有交易，并按交易额排序（从低到高）。");
        List<Transaction> result1 = transactions.stream()
                .filter(t -> t.getYear() == 2019)
                .sorted(Comparator.comparingInt(Transaction::getValue))
                .collect(Collectors.toList());
        System.out.println("1、结果：" + result1);
        //(2) 交易员都在哪些不同的城市工作过？
        System.out.println("2、交易员都在哪些不同的城市工作过？");
        List<String> citys = transactions.parallelStream()
                .map(Transaction::getTrader)
                .map(Trader::getCity)
                .distinct()
                .collect(Collectors.toList());
        System.out.println("2、结果是：" + citys);
        //(3) 查找所有来自于剑桥的交易员，并按姓名排序。
        System.out.println("3、查找所有来自于剑桥的交易员，并按姓名排序");
        List<Trader> result3 = transactions.stream()
                .map(Transaction::getTrader)
                .filter(trader -> "Cambridge".equals(trader.getCity()))
                .distinct()
                .sorted(Comparator.comparing(Trader::getName))
                .collect(Collectors.toList());
        System.out.println("3、结果是：" + result3);
        //(4) 返回所有交易员的姓名字符串，按字母顺序排序。
        System.out.println("4、返回所有交易员的姓名字符串，按字母顺序排序");
        String result4 = transactions.stream()
                .map(transaction -> transaction.getTrader().getName())
                .distinct()
                .sorted()
                .collect(Collectors.joining());
        System.out.println("4、结果是：" + result4);

        //(5) 有没有交易员是在米兰工作的？
        System.out.println("5、有没有交易员是在米兰工作的？");
        boolean anyMatch = transactions.stream()
                .anyMatch(transaction -> "Milan".equals(transaction.getTrader().getCity()));
        System.out.println("5、结果是：" + anyMatch);

        //(6) 打印生活在剑桥的交易员的所有交易额。
        System.out.println("6、打印生活在剑桥的交易员的所有交易额。");
        transactions.stream()
                .filter(transaction -> "Cambridge".equals(transaction.getTrader().getCity()))
                .map(Transaction::getValue)
                .forEach(System.out::println);
        System.out.println("6、结果是见上面打印");

        //(7) 打印生活在剑桥的交易员的所有交易额之和。
        System.out.println("7、打印生活在剑桥的交易员的所有交易额。");
        Integer result7 = transactions.stream()
                .filter(transaction -> "Cambridge".equals(transaction.getTrader().getCity()))
                .map(Transaction::getValue)
                .reduce(0, (v1, v2) -> {
                    System.out.println("v1=" + v1 + ",v2=" + v2);
                    return v1 + v2;
                });
        System.out.println("7、结果是：" + result7);

        //(8) 所有交易中，最高的交易额是多少？
        System.out.println("8、所有交易中，最高的交易额是多少？");
        Optional<Integer> result8 = transactions.stream()
                .map(Transaction::getValue)
                .reduce((x, y) -> x > y ? x : y);
        System.out.println("8、结果是："+result8.get());
        //(9) 找到交易额最小的交易。
        System.out.println("9、找到交易额最小的交易。");
        Optional<Transaction> min = transactions.stream()
                .min(Comparator.comparing(Transaction::getValue));
        System.out.println("9、结果是："+min);

        // 数值流
        System.out.println("数值流计算7");
        int sum = transactions.stream()
                .filter(transaction -> "Cambridge".equals(transaction.getTrader().getCity()))
                .mapToInt(Transaction::getValue)
                .sum();
        System.out.println("数值流计算7的结果："+sum);

        // 转回对象流
        IntStream intStream = transactions.stream()
                .mapToInt(Transaction::getValue);
        // 装箱boxed
        Stream<Integer> boxed = intStream.boxed();

        // 数值流的范围
        // 返回1-100以内的偶数，rangeClosed包含两端，range方法包头不包尾
        IntStream evenNumbers = IntStream.rangeClosed(1, 100)
                .filter(n -> n % 2 == 0);
        System.out.println("数值流的范围，返回1-100以内的偶数"+evenNumbers.count());

        System.out.println("构建流");
        // 下面说几种构建流的方式
        // 1、Stream.of，由静态方法构建流
        Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
        stream.map(String::toUpperCase).forEach(System.out::println);
        // 构建空的流
        Stream<Object> empty = Stream.empty();
        // Arrays.stream 构建流
        int[] numbers = {2, 3, 5, 7, 11, 13};
        int arsum = Arrays.stream(numbers).sum();

        // 文件流
        // 使用Files.lines得到一个流，其中的每个元素都是给定文件中的一行
        // 对line调用split方法将行拆分成单词
        long uniqueWords = 0;
        try(Stream<String> lines =
                    Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
            uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
                    .distinct()
                    .count();
        }
        catch(IOException e){

        }

        // 函数生成无限流，Stream.iterate Stream.generate
        // 无限流一般需要limit限制边界
        // iterate方法接受一个初始值（在这里是0），还有一个依次应用在每个产生的新值上的Lambda（UnaryOperator<t>类型）
        System.out.println("偶数叠加Stream.iterate：");
        Stream.iterate(0, n -> n + 2)
                .limit(10)
                .forEach(System.out::println);
        // 应用：斐波纳契元组序列
        System.out.println("斐波纳契元组序列:");
        Stream.iterate(new int[]{0, 1},
                t -> new int[]{t[1], t[0]+t[1]})
                .limit(10)
                //.forEach(t -> System.out.println("(" + t[0] + "," + t[1] +")"));
                .map(t -> t[0])
                .forEach(System.out::println);
        List<Map> collect = IntStream.range(0, 5).mapToObj(i -> {
            Map map = new HashMap();
            map.put(i + "", i);
            return map;
        }).collect(Collectors.toList());
        System.out.println(collect);

    }
}

/**
 * 交易员
 */
class Trader {
    private final String name;
    private final String city;

    public Trader(String n, String c) {
        this.name = n;
        this.city = c;
    }

    public String getName() {
        return name;
    }

    public String getCity() {
        return city;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Trader{");
        sb.append("name='").append(name).append('\'');
        sb.append(", city='").append(city).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

class Transaction {
    private final Trader trader;
    private final int year;
    private final int value;

    public Transaction(Trader trader, int year, int value) {
        this.trader = trader;
        this.year = year;
        this.value = value;
    }

    public Trader getTrader() {
        return trader;
    }

    public int getYear() {
        return year;
    }

    public int getValue() {
        return value;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Transaction{");
        sb.append("trader=").append(trader);
        sb.append(", year=").append(year);
        sb.append(", value=").append(value);
        sb.append('}');
        return sb.toString();
    }
}